class RegisterMachine:
    def __init__(self, filename, instructionFiles):
        self.program = []
        self.callStack = []
        self.env = {}
        self.labels = {}
        self.instructions = {}
        self.systemInstructions = {}
        self.programCounter = 0
        self.shutdown = False
        
        self.loadSystemInstructions()
        map(self.loadInstructionFile,instructionFiles)
        self.parseFile(filename)
        
    def system_shutdown(self, args):
        self.shutdown = True
        
    def system_call(self, args):
        assert args[0] in self.labels, "unknown label %s" % args[0]
        self.callStack.append(self.programCounter)
        self.programCounter = self.labels[args[0]]
        
    def system_jump(self, args):
        assert args[0] in self.labels, "unknown label %s" % args[0]
        self.programCounter = self.labels[args[0]]
        
    def system_return(self, args):
        assert self.callStack, "stack underrun"
        self.programCounter = self.callStack.pop()
        
    def system_print(self, args):
        #print args, map(self.evalArg,args)
        print ', '.join(map(str,map(self.evalArg,args)))
        
    def system_import(self, args):
        map(self.parseFile,args)
        
    def system_move(self, args):
        self.env[args[0]] = self.evalArg(args[1])
        
    def evalArg(self, arg):
        if arg.startswith("\""):
            return arg
        elif arg.startswith("'"):
            try:
                return eval(arg[1:])
            except:
                return arg[1:]
        else:
            assert arg in self.env, "undefined variable %s" % arg
            return self.env[arg]
        
    def loadSystemInstructions(self):
        for symbol in dir(self):
            if symbol.startswith("system_") and callable(getattr(self,symbol)):
                self.systemInstructions[symbol[len("system_"):]] = getattr(self,symbol)
        
    def loadInstructionFile(self, filename):
        moduleName = filename.rstrip(".py").replace("/",".")
        try:
            exec "import %s as module" % moduleName in locals()
        except ImportError:
            raise RuntimeError, "can't open instruction file %s" % filename
        
        for symbol in dir(module):
            if callable(getattr(module,symbol)):
                assert symbol not in self.systemInstructions,\
                    "instruction %s in file %s conflicts with a system instruction" %\
                    (symbol,filename)
                self.instructions[symbol] = getattr(module,symbol)
                    
    def tokenize(self, line):
        toks = []
        currentTok = ""
        inQuotes = False
        for char in line:
            if char.isspace():
                if inQuotes:
                    currentTok += char
                else:
                    if currentTok:
                        toks.append(currentTok)
                    currentTok = ""
            elif char == "\"":
                if inQuotes:
                    currentTok += char
                if currentTok:
                    toks.append(currentTok)
                currentTok = ""
                if not inQuotes:
                    currentTok += char
                inQuotes = not inQuotes
            else:
                currentTok += char
        if currentTok:
            toks.append(currentTok)
            
        return toks
        
    def parseFile(self, filename):
        for lineNumber, line in enumerate(open(filename,"r").readlines()):
            commentPos = line.find("#")
            if commentPos != -1:
                line = line[:commentPos]
                
            line = line.strip()
            
            if line:
                
                labelPos = line.find(":")
                if labelPos != -1:
                    label = line[:labelPos].strip()
                    line = line[labelPos+1:].strip()
                    self.labels[label] = len(self.program)
                    
                toks = self.tokenize(line)
                assert toks[0] in self.instructions or toks[0] in self.systemInstructions, \
                    "unknown instruction %s at line %d in file %s" % (toks[0],lineNumber+1,filename)
                self.program.append(toks)
                
    def run(self):
        while self.programCounter < len(self.program) and not self.shutdown:
            toks = self.program[self.programCounter]
            self.programCounter += 1
            
            cmd = toks[0]
            args = toks[1:]
            
            if cmd in self.systemInstructions:
                self.systemInstructions[cmd](args)
            elif cmd in self.instructions:
                result = self.instructions[cmd](map(self.evalArg,args))
                if result:
                    self.env['result'] = result
            else:
                raise RuntimeError, "unknown command %s" % cmd
